package org.dant.ant.extension.tasks;

import java.io.*;
import java.util.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.optional.junit.DOMUtil;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.util.DOMElementWriter;
import org.apache.tools.ant.util.StringUtils;
import org.dant.ant.extension.xml.AggregateTransformer;
import org.dant.ant.extension.xml.XMLConstants;
import org.xml.sax.SAXException;

public class DantReport extends Task implements XMLConstants {
    public static final String DEFAULT_DIR = ".";
    public static final String DEFAULT_FILENAME = "TESTS-TestSuites.xml";
  
    protected Vector filesets = new Vector();

    protected String toFile;

    protected File toDir;

    protected Vector transformers = new Vector();

    private boolean failOnError;
    private Vector failedTests = new Vector();

    public AggregateTransformer createReport() {
        AggregateTransformer transformer = new AggregateTransformer(this);
        transformers.addElement(transformer);
        return transformer;
    }
  
    public void setTofile(String value) {
        toFile = value;
    }
  
    public void setTodir(File value) {
        toDir = value;
    }
  
    public void setFailOnError(boolean failonerror) {
        this.failOnError = failonerror;
    }
  
    public void addFileSet(FileSet fs) {
        filesets.addElement(fs);
    }
  
    public void execute() throws BuildException {
        Element rootElement = createDocument();
        File destFile = getDestinationFile();
        try {
            writeDOMTree(rootElement.getOwnerDocument(), destFile);
        } catch (IOException e) {
            throw new BuildException("Unable to write test aggregate to '" + destFile + "'", e);
        }

        Enumeration elements = transformers.elements();
        while (elements.hasMoreElements()) {
            AggregateTransformer transformer =
                (AggregateTransformer) elements.nextElement();
            transformer.setXmlDocument(rootElement.getOwnerDocument());
            transformer.transform();
        }
        if (failedTests.size() > 0) {
      
            log("The following tests failed:", Project.MSG_WARN);
            Enumeration e = failedTests.elements();
            while (e.hasMoreElements()) {
                log((String)e.nextElement(), Project.MSG_WARN);
            }
            if (failOnError) {
                throw new BuildException("Failed tests detected!");
            }
      
        }
    }
  
    protected File getDestinationFile() {
        if (toFile == null) {
            toFile = DEFAULT_FILENAME;
        }
        if (toDir == null) {
            toDir = project.resolveFile(DEFAULT_DIR);
        }
        return new File(toDir, toFile);
    }
  
    protected File[] getFiles() {
        Vector v = new Vector();
        final int size = filesets.size();
        for (int i = 0; i < size; i++) {
            FileSet fs = (FileSet) filesets.elementAt(i);
            DirectoryScanner ds = fs.getDirectoryScanner(project);
            ds.scan();
            String[] f = ds.getIncludedFiles();
            for (int j = 0; j < f.length; j++) {
                String pathname = f[j];
                if (pathname.endsWith(".xml")) {
                    File file = new File(ds.getBasedir(), pathname);
                    file = project.resolveFile(file.getPath());
                    v.addElement(file);
                }
            }
        }
    
        File[] files = new File[v.size()];
        v.copyInto(files);
        return files;
    }
  
    protected void writeDOMTree(Document doc, File file) throws IOException {
        OutputStream out = null;
        PrintWriter wri = null;
        try {
            out = new FileOutputStream(file);
            wri = new PrintWriter(new OutputStreamWriter(out, "UTF8"));
            wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
            (new DOMElementWriter()).write(doc.getDocumentElement(), wri, 0, "  ");
            wri.flush();
            // writers do not throw exceptions, so check for them.
            if (wri.checkError()) {
                throw new IOException("Error while writing DOM content");
            }
        } finally {
            if (wri != null) {
                wri.close();
                out = null;
            }
            if (out != null) {
                out.close();
            }
        }
    }
  
    protected Element createDocument() {
        DocumentBuilder builder = getDocumentBuilder();
        Document doc = builder.newDocument();
        Element rootElement = doc.createElement(TESTSUITES);
        doc.appendChild(rootElement);
    
        File[] files = getFiles();
        for (int i = 0; i < files.length; i++) {
            try {
                log("Parsing file: '" + files[i] + "'", Project.MSG_VERBOSE);
                //XXX there seems to be a bug in xerces 1.3.0 that doesn't like file object
                // will investigate later. It does not use the given directory but
                // the vm dir instead ? Works fine with crimson.
                Document testsuiteDoc = builder.parse("file:///" + files[i].getAbsolutePath());
                Element elem = testsuiteDoc.getDocumentElement();
                // make sure that this is REALLY a testsuite.
                if (TESTCASE.equals(elem.getNodeName())
                    || TASK.equals(elem.getNodeName())
                    || TESTSUITE.equals(elem.getNodeName())) {
          
                    Element copy = (Element) DOMUtil.importNode(rootElement, elem);
                    String result = elem.getAttribute(ATTR_RESULT).toUpperCase();
                    if ("FAILED".equals(result)) {
                        String pkg = elem.getAttribute(ATTR_PACKAGE);
                        String name = elem.getAttribute(ATTR_NAME);
                        failedTests.add(pkg + "/" + name);
                    }
                } else if (TESTSUITE.equals(elem.getNodeName())) {
                    // add suite title and raw output dir to RESULTS
                    rootElement.setAttribute(ATTR_NAME, elem.getAttribute(ATTR_NAME));
                    //  rootElement.setAttribute(ATTR_RAWDATA, elem.getAttribute(ATTR_OWNER));
                } else {
                    // issue a warning.
                    log("the file " + files[i] + " is not a valid testsuite XML document",
                        Project.MSG_WARN);
                }
            } catch (SAXException e) {
                // a testcase might have failed and write a zero-length document,
                // It has already failed, but hey.... mm. just put a warning
                log("The file " + files[i] + " is not a valid XML document. It is possibly corrupted.",
                    Project.MSG_WARN);
                log(StringUtils.getStackTrace(e), Project.MSG_DEBUG);
            } catch (IOException e) {
                log("Error while accessing file " + files[i] + ": " + e.getMessage(), Project.MSG_ERR);
            }
        }
        return rootElement;
    }
  
    private static DocumentBuilder getDocumentBuilder() {
        try {
            return DocumentBuilderFactory.newInstance().newDocumentBuilder();
        } catch (Exception exc) {
            throw new ExceptionInInitializerError(exc);
        }
    }
}

